package com.boot.adminservice.map.service.impl;


import com.boot.adminapi.map.constants.MapConstants;
import com.boot.adminapi.map.domain.dto.LocationReqDTO;
import com.boot.adminapi.map.domain.dto.PlaceSearchReqDTO;
import com.boot.adminservice.config.service.SysArgumentService;
import com.boot.adminservice.map.domain.dto.*;
import com.boot.adminservice.map.domain.entity.SysRegion;
import com.boot.adminservice.map.mapper.RegionMapper;
import com.boot.adminservice.map.service.MapService;
import com.boot.adminservice.map.service.TencentMapProvider;
import com.boot.adminservice.map.service.strategy.GetCityListContext;
import com.boot.commoncache.utils.CacheUtil;
import com.boot.commoncore.domain.dto.BasePageDTO;
import com.boot.commoncore.utils.BeanCopyUtil;
import com.boot.commoncore.utils.PageUtil;
import com.boot.commonredis.service.RedisService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.benmanes.caffeine.cache.Cache;
import net.sourceforge.pinyin4j.PinyinHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 地图服务层的实现类
 */
@Service
public class MapServiceImpl implements MapService {

    /**
     * sys_region的mapper
     */
    @Autowired
    private RegionMapper regionMapper;

    /**
     * redis服务对象
     */
    @Autowired
    private RedisService redisService;

    /**
     * 本地缓存服务对象
     */
    @Autowired
    private Cache<String, Object> caffeineCache;

    /**
     * 策略模式上下文对象:获取城市列表策略和获取A-Z分类城市列表策略
     */
    @Autowired
    private GetCityListContext getCityListContext;

    /**
     * 腾讯位置服务封装对象
     */
    @Autowired
    private TencentMapProvider tencentMapProvider;

    /**
     * 参数服务对象
     */
    @Autowired
    private SysArgumentService sysArgumentService;
    /**
     * 全量区划数据持久化到mysql
     */
    @Override
    @Transactional
    public boolean saveTencentRegionData() {
        try{
            // 1 调用地图位置查询接口(只有一个参数accseeKey,已经被封装到tencentMapProvider,无需再构造传参对象)
            TencentRegionResponseDTO tencentRegionResponseDTO = tencentMapProvider.saveRegionData();
            // 2 数据转换 + 层级标记
            List<SysRegion> sysRegions = convertAndSetLevel(tencentRegionResponseDTO.getResult());
            // 3 批量插入（获取自增ID）
            regionMapper.batchInsert(sysRegions);
            // 4 构建 code => id 的映射表
            Map<String, Long> codeToIdMap = sysRegions.stream()
                    .collect(Collectors.toMap(SysRegion::getCode,  SysRegion::getId));
            // 5 设置父子关系并更新
            setParentRelationships(sysRegions, codeToIdMap);
        } catch(Exception e){
            return false;
        }
        return true;
    }

    /**
     *
     * @param regions 外层List元素有三个(分别是省市区List)，内层List的元素是具体行政单位TencentRegionDTO
     * @return 要插入mysql的包含SysRegion的List
     */
    private List<SysRegion> convertAndSetLevel(List<List<TencentRegionDTO>> regions) {
        List<SysRegion> result = new ArrayList<>();

        // 处理省级数据（下标0）
        result.addAll(regions.get(0).stream()
                .filter(region -> isValidRegionCode(region.getId()))
                .map(region -> createRegion(region, 1)) // level=1
                .collect(Collectors.toList()));

        // 处理市级数据（下标1）
        result.addAll(regions.get(1).stream()
                .filter(region -> isValidRegionCode(region.getId()))
                .map(region -> createRegion(region, 2)) // level=2
                .collect(Collectors.toList()));

        // 处理区级数据（下标2）
        result.addAll(regions.get(2).stream()
                .filter(region -> isValidRegionCode(region.getId()))
                .map(region -> {
                    SysRegion sysRegion = createRegion(region, 3); // level=3
                    // 区级特殊处理
                    sysRegion.setName(region.getFullname());  // name = fullname
                    sysRegion.setPinyin(convertToPinyin(region.getFullname()));  // 转换为拼音
                    return sysRegion;
                })
                .collect(Collectors.toList()));

        return result;
    }

    /**
     * 中文转拼音
     * @param chinese 中文字符串
     * @return 拼音字符串
     */
    private String convertToPinyin(String chinese) {
        StringBuilder pinyin = new StringBuilder();
        for (char c : chinese.toCharArray())  {
            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);
            if (pinyinArray != null && pinyinArray.length  > 0) {
                pinyin.append(pinyinArray[0].replaceAll("\\d",  "")); // 去除声调数字
            } else {
                pinyin.append(c);
            }
            pinyin.append(" ");
        }
        return pinyin.toString().trim();
    }
    /**
     * 创建基础行政区划对象，暂时不处理区级行政区域的name和pinyin属性(腾讯位置服务返回的区级这两个属性为null)
     * @param region 区划对象TencentRegion
     * @param level 级别(省1市2区3)
     * @return
     */
    private SysRegion createRegion(TencentRegionDTO region, int level) {
        SysRegion sysRegion = new SysRegion();
        sysRegion.setCode(region.getId());
        sysRegion.setName(region.getName());
        sysRegion.setFullName(region.getFullname());
        sysRegion.setPinyin(region.getPinyin()  != null ?
                String.join(" ", region.getPinyin())  : "");
        sysRegion.setLongitude(region.getLocation().getLng());
        sysRegion.setLatitude(region.getLocation().getLat());
        sysRegion.setLevel(level);
        return sysRegion;
    }

    /**
     * 校验行政区划代码有效性
     * @param code 要校验的行政区划编码
     * @return 是否符合编码规则的布尔值
     */
    private boolean isValidRegionCode(String code) {
        return code != null && code.matches("\\d{6}")  && !"999999".equals(code);
    }

    /**
     * 再次查询数据库设置行政区划的父子关系
     * @param sysRegions 数据库返回的行政区划List
     * @param codeToIdMap 行政区划的编码:主键id
     */
    private void setParentRelationships(List<SysRegion> sysRegions, Map<String, Long> codeToIdMap) {
        for (SysRegion region : sysRegions) {
            String parentCode = calculateParentCode(region.getCode(),  region.getLevel());
            region.setParentCode(parentCode);
            region.setParentId(codeToIdMap.getOrDefault(parentCode,  0L)); // 0表示无父级
            regionMapper.updateParentInfo(region);
        }
    }

    /**
     * 计算父级编码:查询编码对应的等级判断父级等级
     * @param currentCode 行政编码
     * @param level 区划等级
     * @return 父级行政编码
     */
    private String calculateParentCode(String currentCode, Integer level) {
        switch (level) {
            case 1: return "0"; // 省级无父级
            case 2: return currentCode.substring(0,  2) + "0000"; // 市级=>所属省
            case 3:
                // 区级：优先判断是否是直辖市辖区或省直辖县
                if (isDirectMunicipalityDistrict(currentCode) ||
                        "90".equals(currentCode.substring(2,  4))) {
                    return currentCode.substring(0,  2) + "0000";
                }
                return currentCode.substring(0,  4) + "00"; // 普通区县=>所属市
            default: return "0";
        }
    }

    /**
     * 判断是否为直辖市辖区（110101=>北京东城区）
     * @param code 行政编码
     * @return 是否是直辖市辖区的布尔值
     */
    private boolean isDirectMunicipalityDistrict(String code) {
        return code.startsWith("11")  || code.startsWith("12")  || // 京津
                code.startsWith("31")  || code.startsWith("50");    // 沪渝
    }

    /**
     * 通过策略模式获取城市列表
     * @return 城市列表
     */
    @Override
    public List<SysRegionDTO> getCityList() {
        return getCityListContext.getCityList();
    }

    /**
     * 城市拼音归类查询
     * @return 城市字母与城市列表的哈希
     */
    @Override
    public Map<String, List<SysRegionDTO>> getCityPyList() {
        return getCityListContext.getCityPyList();
    }

    /**
     * 根据父级区域ID获取子集区域列表
     * @param parentId 父级区域ID
     * @return 子集区域列表
     */
    @Override
    public List<SysRegionDTO> getRegionChildren(Long parentId) {
        // 1 入参可以参与构建缓存的key
        String key = MapConstants.CACHE_MAP_CITY_CHILDREN_KEY +parentId;
        // 2 查缓存
        List<SysRegionDTO> resultRegions = CacheUtil.getL2Cache(redisService, key, new TypeReference<List<SysRegionDTO>>() {
        }, caffeineCache);
        if (resultRegions != null) {
            return resultRegions;
        }
        // 3 查询数据库，把parentId与入参相等的数据拿出来
        List<SysRegion> list = regionMapper.selectAllRegion();
        List<SysRegionDTO> result = new ArrayList<>();
        for (SysRegion sysRegion : list) {
            if (sysRegion.getParentId() != 0 && sysRegion.getParentId().equals(parentId)) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                result.add(sysRegionDTO);
            }
        }
        // 4 设置缓存
        CacheUtil.setL2Cache(redisService, key, result, caffeineCache, 120L, TimeUnit.MINUTES);
        return result;
    }

    /**
     * 获取热门城市列表
     * @return 城市列表
     */
    @Override
    public List<SysRegionDTO> getHotCityList() {

        // 1 先查缓存
        List<SysRegionDTO> hotCityList = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_HOT_CITY, new TypeReference<List<SysRegionDTO>>() {
        }, caffeineCache);
        if (hotCityList != null) {
            return hotCityList;
        }

        // 2 设置6个热门城市(北上广深、杭州、成都)
        String ids = sysArgumentService.getByConfigKey(MapConstants.CONFIG_KEY).getValue();
        List<Long> idList = new ArrayList<>();
        for (String num : ids.split(",")) {
            idList.add(Long.parseLong(num));
        }
        // 3 查询热门城市结果
        List<SysRegionDTO> result = new ArrayList<>();
        for (SysRegion sysRegion : regionMapper.selectBatchIds(idList)) {
            SysRegionDTO sysRegionDTO = new SysRegionDTO();
            BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
            result.add(sysRegionDTO);
        }
        // 4 设置缓存
        CacheUtil.setL2Cache(redisService, MapConstants.CACHE_MAP_HOT_CITY, result, caffeineCache, 120L, TimeUnit.MINUTES);
        return result;
    }

    /**
     * 根据地点搜索
     * @param placeSearchReqDTO 搜索条件
     * @return 搜索结果
     */
    @Override
    public BasePageDTO<SearchPoiDTO> searchSuggestOnMap(PlaceSearchReqDTO placeSearchReqDTO) {
        // 1 构建查询腾讯位置服务的入参
        SuggestSearchDTO suggestSearchDTO = new SuggestSearchDTO();
        BeanCopyUtil.copyProperties(placeSearchReqDTO, suggestSearchDTO);
        suggestSearchDTO.setPageIndex(placeSearchReqDTO.getPageNo());
        suggestSearchDTO.setId(String.valueOf(placeSearchReqDTO.getId()));
        // 2 调用地图位置查询接口
        PoiListDTO poiListDTO = tencentMapProvider.searchTencentMapPlaceByRegion(suggestSearchDTO);
        // 3 做结果对象转换
        List<PoiDTO> poiDTOList = poiListDTO.getData();
        BasePageDTO<SearchPoiDTO> result = new BasePageDTO<>();
        result.setTotals(poiListDTO.getCount());
        result.setTotalPages(PageUtil.getTotalPages(result.getTotals(), placeSearchReqDTO.getPageSize()));

        List<SearchPoiDTO> pageRes = new ArrayList<>();
        for (PoiDTO poiDTO : poiDTOList) {
            SearchPoiDTO searchPoiDTO = new SearchPoiDTO();
            BeanCopyUtil.copyProperties(poiDTO, searchPoiDTO);
            searchPoiDTO.setLongitude(poiDTO.getLocation().getLng());
            searchPoiDTO.setLatitude(poiDTO.getLocation().getLat());
            pageRes.add(searchPoiDTO);
        }
        result.setList(pageRes);
        return result;
    }

    /**
     * 根据经纬度来定位城市
     * @param locationReqDTO 经纬度信息
     * @return 城市信息
     */
    @Override
    public RegionCityDTO getCityByLocation(LocationReqDTO locationReqDTO) {
        // 1 构建查询腾讯位置服务的入参
        LocationDTO locationDTO = new LocationDTO();
        BeanCopyUtil.copyProperties(locationReqDTO, locationDTO);
        RegionCityDTO result = new RegionCityDTO();
        // 2 调用腾讯位置服务接口
        GeoResultDTO geoResultDTO =  tencentMapProvider.getTencentMapDistrictByLonLat(locationDTO);
        if (geoResultDTO != null && geoResultDTO.getResult() != null && geoResultDTO.getResult().getAd_info() != null) {
            String cityName = geoResultDTO.getResult().getAd_info().getCity();
            // 3 查缓存
            List<SysRegionDTO> cache = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_CITY_KEY, new TypeReference<List<SysRegionDTO>>() {
            }, caffeineCache);
            for (SysRegionDTO sysRegionDTO: cache) {
                if (sysRegionDTO.getFullName().equals(cityName)) {
                    BeanCopyUtil.copyProperties(sysRegionDTO, result);
                    return result;
                }
            }
        }
        return result;
    }

}
